1 /* 2 Copyright: Marcelo S. N. Mancini (Hipreme|MrcSnm), 2018 - 2021 3 License: [https://creativecommons.org/licenses/by/4.0/|CC BY-4.0 License]. 4 Authors: Marcelo S. N. Mancini 5 6 Copyright Marcelo S. N. Mancini 2018 - 2021. 7 Distributed under the CC BY-4.0 License. 8 (See accompanying file LICENSE.txt or copy at 9 https://creativecommons.org/licenses/by/4.0/ 10 */ 11 module hip.util.file; 12 import hip.util.conv:to; 13 import hip.util.array:join, array; 14 import hip.util.system; 15 import hip.util.path; 16 import hip.util.string; 17 18 19 string getFileContent(string path, bool noCarriageReturn = true) 20 { 21 import core.stdc.stdio; 22 path = sanitizePath(path); 23 FILE* file = fopen((path~"\0").ptr, "r"); 24 if(!file) 25 return ""; 26 char[] buffer; 27 28 fseek(file, 0, SEEK_END); 29 auto size = ftell(file); 30 fseek(file, 0, SEEK_SET); 31 32 buffer.length = cast(typeof(buffer.length))size; 33 size_t readSize = fread(buffer.ptr, cast(size_t)size, 1, file); 34 if(readSize != buffer.length) 35 buffer.length = readSize; 36 fclose(file); 37 38 string content = cast(string)buffer; 39 return (noCarriageReturn) ? content.replaceAll('\r') : content; 40 } 41 42 version(Windows) 43 { 44 string getcwd() 45 { 46 import core.sys.windows.winnt:MAX_PATH; 47 import core.sys.windows.winbase; 48 char[MAX_PATH] buffer; 49 50 uint length = GetCurrentDirectoryA(MAX_PATH, buffer.ptr); 51 52 char[] ret = new char[](length); 53 ret[] = buffer[0..length]; 54 55 return cast(string)ret; 56 } 57 } 58 else 59 { 60 string getcwd() 61 { 62 import std.file; 63 return std.file.getcwd(); 64 } 65 } 66 67 68 69 string stripLineBreaks(string content) 70 { 71 content = content.replaceAll('\r'); 72 content = content.replaceAll('\n'); 73 return content; 74 } 75 76 // string getFileContentFromBasePath(string path, string basePath, bool noCarriageReturn = true) 77 // { 78 // string finalPath = relativePath(sanitizePath(path), sanitizePath(basePath)); 79 // return getFileContent(finalPath, noCarriageReturn); 80 // } 81 82 83 84 version(none) 85 { 86 import std.stdio:File; 87 88 version(CustomRuntimeTest) version = CustomRuntime; 89 version(WebAssembly) version = CustomRuntime; 90 version(PSVita) version = CustomRuntime; 91 92 version(CustomRuntimeTest) {void fileTruncate(File file, ptrdiff_t offset){}} 93 else 94 { 95 import std.file; 96 void fileTruncate(File file, ptrdiff_t offset) 97 { 98 version (Windows) 99 { 100 import hip.util.windows; 101 file.seek(offset); 102 if(!SetEndOfFile(file.windowsHandle())) 103 throw new FileException(file.name, "SetEndOfFile error"); 104 } 105 106 version (Posix) 107 { 108 import core.sys.posix.unistd: ftruncate; 109 int res = ftruncate(file.fileno(), offset); 110 if(res != 0) 111 throw new FileException(file.name, "ftruncate error with code "~to!string(res)); 112 } 113 } 114 } 115 } 116 117 version(none) class FileProgression 118 { 119 protected ulong progress; 120 protected uint stepSize; 121 protected ulong fileSize; 122 protected void delegate(ref ubyte[] data) onFinish; 123 protected void delegate(float progress) onUpdate; 124 ubyte[] fileData; 125 ubyte[] buffer; 126 File target; 127 128 /** 129 * If bytes == 0, it will use readsteps. 130 * 131 * Readsteps default is by progressing from 0 to 100, which makes great for percentage. Also notice that with 100 readsteps there's almost 132 * no loss compared to reading the file in one go, so it is the recommended value. 133 * 134 * The greater the readsteps, the more time it will take to read the file and the more precision the percentage will have. Usually 100 should be enough. 135 * 136 * If the readsteps are greater than the filesize, it will clamp to fileSize as readsteps. 137 * That means it will update at every byte 138 */ 139 this(string filePath, uint readSteps = 100, uint bytes = 0) 140 { 141 assert(readSteps != 0 || bytes != 0, "Can't have readSteps and bytes both == 0"); 142 progress = 0; 143 target = File(filePath, "r"); 144 ulong fSize = fileSize = target.size; 145 assert(cast(uint)fSize < uint.max, "Filesize is greater than uint.max, contact the FileProgression mantainer"); 146 fileData.reserve(cast(uint)fSize); 147 148 if(readSteps > fSize) 149 readSteps = cast(uint)fSize; 150 if(bytes != 0) 151 readSteps = cast(uint)fSize/bytes; 152 153 real sz =cast(real)fSize/readSteps; 154 if(sz != fSize/readSteps) //Odd 155 { 156 size_t remaining = cast(size_t)((sz-(fSize/readSteps))*readSteps); 157 buffer = new ubyte[remaining]; 158 target.rawRead(buffer); 159 fileData~= buffer[]; 160 progress+= remaining; 161 stepSize = cast(uint)(fSize-remaining)/readSteps; 162 } 163 else 164 stepSize = cast(uint)fSize/readSteps; 165 buffer.length = stepSize; 166 } 167 168 void setOnFinish(void delegate(ref ubyte[] data) onFinish){this.onFinish = onFinish;} 169 void setOnUpdate(void delegate(float progress) onUpdate){this.onUpdate = onUpdate;} 170 171 bool update() 172 { 173 target.rawRead(buffer); 174 fileData~= buffer[]; 175 progress+=stepSize; 176 if(onUpdate) 177 onUpdate(getProgress()); 178 bool finished = progress >= fileSize; 179 if(finished) 180 { 181 target.close(); 182 if(onFinish) 183 onFinish(this.fileData); 184 } 185 186 return !finished; 187 } 188 189 190 float getProgress(){return progress/cast(float)fileSize;} 191 @property ulong readSize(){return fileData.length;} 192 @property ulong size(){return fileSize;} 193 194 195 override string toString(){return cast(string)fileData;} 196 }